package com.dream.utils.hibernate;

import java.util.Collection;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;
import org.hibernate.sql.JoinType;
/**
 * @copyright evan
 * @author evan
 * @Revision
 * @date 2012/9/22
 */
public class DetachedCriteriaBuilder {


	/** The detached criteria. */
	private DetachedCriteria detachedCriteria;

	private DetachedCriteriaBuilder(DetachedCriteria detachedCriteria) {

		this.detachedCriteria = detachedCriteria;

	}

	/**
	 * instance class with model type 
	 * @param detachedCriteria
	 * @return
	 */
	public static DetachedCriteriaBuilder instance(DetachedCriteria detachedCriteria) {

		return new DetachedCriteriaBuilder(detachedCriteria);
	}
	
	/**
	 * instance class
	 * @param clazz
	 * @return
	 */
	public static DetachedCriteriaBuilder instance(Class<?> clazz) {

		return new DetachedCriteriaBuilder(DetachedCriteria.forClass(clazz));
	}

	/**
	 * instance class with model type & alias
	 * @param clazz
	 * @param alias
	 * @return
	 */
	public static DetachedCriteriaBuilder instance(Class<?> clazz, String alias) {

		return new DetachedCriteriaBuilder(DetachedCriteria.forClass(clazz, alias));
	}
	
	private boolean isNotEmptyString(Object obj){
		
		return !(null == obj || (String.class.equals(obj.getClass()) && StringUtils.isEmpty(String.valueOf(obj).trim())));
		
	}
	
	/**
	 * equal expression sql ==
	 * @param property
	 * @param value
	 * @return
	 */
	public DetachedCriteriaBuilder addEq(String property, Object value) {

		if (isNotEmptyString(value)) {
			detachedCriteria.add(Property.forName(property).eq(value));
		}
		return this;
	}
	/**
	 * not equal expression sql !=
	 * @param property
	 * @param value
	 * @return
	 */
	public DetachedCriteriaBuilder addNe(String property, Object value) {
		
		if (isNotEmptyString(value)) {
			this.detachedCriteria.add(Property.forName(property).ne(value));
		}
		return this;
	}
	
	/**
	 * low than sql <
	 * @param property
	 * @param value
	 * @return
	 */
	public DetachedCriteriaBuilder addLt(String property, Object value) {

		if (isNotEmptyString(value)) {
			detachedCriteria.add(Property.forName(property).lt(value));
		}

		return this;
	}

	/**
	 * low than or equal sql <=
	 * @param property
	 * @param value
	 * @return
	 */
	public DetachedCriteriaBuilder addLe(String property, Object value) {

		if (isNotEmptyString(value)) {
			detachedCriteria.add(Property.forName(property).le(value));
		}
		
		return this;
	}

	/**
	 * great than sql >
	 * @param property
	 * @param value
	 * @return
	 */
	public DetachedCriteriaBuilder addGt(String property, Object value) {

		if (isNotEmptyString(value)) {
			detachedCriteria.add(Property.forName(property).gt(value));
		}
		return this;
	}

	/**
	 * great than or equal sql >=
	 * @param property
	 * @param value
	 * @return
	 */
	public DetachedCriteriaBuilder addGe(String property, Object value) {

		if (isNotEmptyString(value)) {
			detachedCriteria.add(Property.forName(property).ge(value));
		}
		
		return this;
	}

	/**
	 * between from & to sql between
	 * @param property
	 * @param start
	 * @param end
	 * @return
	 */
	public DetachedCriteriaBuilder addBetween(String property, Object from, Object to) {

		if (null != from && null != to) {
			detachedCriteria.add(Property.forName(property).between(from, to));
		}

		return this;
	}
	
	/**
	 * sql in
	 * @param property
	 * @param values
	 * @return
	 */
	public DetachedCriteriaBuilder addIn(String property, Object[] values) {

		if (ArrayUtils.isNotEmpty(values)) {
			detachedCriteria.add(Property.forName(property).in(values));
		}
		return this;
	}

	/**
	 * sql in
	 * @param property
	 * @param values
	 * @return
	 */
	public DetachedCriteriaBuilder addIn(String property, Collection<?> values) {

		if (CollectionUtils.isNotEmpty(values)) {
			detachedCriteria.add(Property.forName(property).in(values));
		}

		return this;
	}

	/**
	 * add is null 
	 * @param property
	 * @return
	 */
	public DetachedCriteriaBuilder addIsNull(String property) {

		this.detachedCriteria.add(Property.forName(property).isNull());
		return this;
	}

	/**
	 * add is not null
	 * @param property
	 * @return
	 */
	public DetachedCriteriaBuilder addIsNotNull(String property) {

		this.detachedCriteria.add(Property.forName(property).isNotNull());
		return this;
	}
	
	/**
	 * left join
	 * @param property
	 * @param alias
	 * @return
	 */
	public DetachedCriteriaBuilder leftJoin(String property, String alias) {
		this.detachedCriteria.createAlias(property, alias, JoinType.LEFT_OUTER_JOIN);
		return this;
	}

	/**
	 * inner join
	 * @param property
	 * @param alias
	 * @return
	 */
	public DetachedCriteriaBuilder innerJoin(String property, String alias) {
		this.detachedCriteria.createAlias(property, alias, JoinType.INNER_JOIN);
		return this;
	}

	/**
	 * inner join
	 * @param property
	 * @param alias
	 * @return
	 */
	public DetachedCriteriaBuilder createAliasInnerJoin(String property, String alias) {

		this.detachedCriteria.createAlias(property, alias);

		return this;
	}
	
	/**
	 * set property
	 * @param property
	 * @return
	 */
	public DetachedCriteriaBuilder setGetProperty(String property) {

		this.detachedCriteria.setProjection(Projections.property(property));
		return this;
	}

	/**
	 * add asc order
	 * @param property
	 * @return
	 */
	public DetachedCriteriaBuilder addAscOrder(String property) {
		
		this.detachedCriteria.addOrder(Order.asc(property));
		return this;
	}

	/**
	 * add desc order
	 * @param property
	 * @return
	 */
	public DetachedCriteriaBuilder addDescOrder(String property) {
		
		this.detachedCriteria.addOrder(Order.desc(property));
		return this;
		
	}

	/**
	 * add like case  like %value%
	 * @param property
	 * @param value
	 * @return
	 */
	public DetachedCriteriaBuilder addLikeAny(String property, String value) {

		if (StringUtils.isNotBlank(value)) {

			this.detachedCriteria.add(Property.forName(property).like(value.trim(), MatchMode.ANYWHERE));
		}

		return this;
	}
	
	/**
	 * add like case  like value%
	 * @param property
	 * @param value
	 * @return
	 */
	public DetachedCriteriaBuilder addLikeStart(String property, String value ){
		
		if (StringUtils.isNotBlank(value)) {

			this.detachedCriteria.add(Property.forName(property).like(value.trim(), MatchMode.START));
		}

		return this;
	}
	
	/**
	 *  add like case  like %value
	 * @param property
	 * @param value
	 * @return
	 */
	public DetachedCriteriaBuilder addLikeEnd(String property, String value ){
		
		if (StringUtils.isNotBlank(value)) {
			this.detachedCriteria.add(Property.forName(property).like(value.trim(), MatchMode.END));
		}

		return this;
	}

	/**
	 * add or 
	 * @param property
	 * @param lValue
	 * @param rValue
	 * @return
	 */
	public DetachedCriteriaBuilder addOr(String property, Object lValue, Object rValue) {
		this.detachedCriteria.add(Restrictions.or(Restrictions.eq(property, lValue), Restrictions.eq(property, rValue)));
		return this;
	}

	/**
	 * add or
	 * @param propertyOne
	 * @param valueOne
	 * @param propertyTwo
	 * @param valueTwo
	 * @return
	 */
	public DetachedCriteriaBuilder addOr(String propertyOne, Object valueOne, String propertyTwo, Object valueTwo) {
		this.detachedCriteria.add(Restrictions.or(Restrictions.eq(propertyOne, valueOne),
				Restrictions.eq(propertyTwo, valueTwo)));
		return this;
	}

	/**
	 * get detached criteria
	 * 
	 * @return the detached criteria
	 */
	public DetachedCriteria getDetachedCriteria() {
		return detachedCriteria;
	}

	/**
	 * set projection
	 * @param pj
	 * @return
	 */
	public DetachedCriteriaBuilder setProjection(Projection pj) {
		this.detachedCriteria.setProjection(pj);
		return this;
	}

	/**
	 * set order
	 * @param orderName
	 * @return
	 */
	public DetachedCriteriaBuilder setOrder(String orderName) {
		if (StringUtils.isNotBlank(orderName)) {
			for (String order : orderName.split("\\|")) {
				String[] orderField = order.split(",");
				if (orderField.length == 1) {
					this.addAscOrder(orderField[0]);
				}
				if (orderField.length == 2) {
					if (StringUtils.lowerCase(orderField[1]).equals("asc")) {
						this.addAscOrder(orderField[0]);
					} else {
						this.addDescOrder(orderField[0]);
					}
				}
				
			}
		}
		return this;
	}
}